#include <my_head.h>

typedef struct _Msg{
    int len;
    char buff[128];
}msg_t;

int main(int argc, const char *argv[]){
    if(3 != argc){
        printf("Usage : %s <IP> <PORT>\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    // 创建套接字
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(-1 == sockfd)
        ERR_LOG("socket error");
    
    // 填充服务器网络信息结构体
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(atoi(argv[2]));
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    socklen_t serveraddr_len = sizeof(serveraddr);

    // 将套接字和服务器网络信息结构体绑定
    if(-1 == bind(sockfd, (struct sockaddr *)&serveraddr, serveraddr_len))
        ERR_LOG("bind error");

    // 将套接字设置成被动监听状态
    if(-1 == listen(sockfd, 5))
        ERR_LOG("listen error");
    
    // 定义结构体 保存客户端的信息
    struct sockaddr_in clientaddr;
    socklen_t clientaddr_len = sizeof(clientaddr);

    int nbytes = 0;
    char buff[128] = {0};
    char filename[32] = {0};
    int fd = 0;
    // 阻塞等待客户端连接
    int acceptfd = 0;
    while(1){
        printf("正在等待客户端连接...\n");
        if(-1 == (acceptfd = accept(sockfd, (struct sockaddr *)&clientaddr, &clientaddr_len))){
            ERR_LOG("accept error");
        }
        printf("客户端[%s:%d]连接到服务器..\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
        // 接收客户端发来的 文件名
RE_NAME:
        if(-1 == (nbytes = recv(acceptfd, filename, sizeof(filename), 0))){
            ERR_LOG("recv error");
        }else if(0 == nbytes){
            printf("客户端[%s:%d]断开连接..\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
            continue;
        }
        printf("客户端要下载的文件是 [%s]\n", filename);
        // 打开客户端要下载的文件
        if(-1 == (fd = open(filename, O_RDONLY))){
            if(ENOENT == errno){ // 说明是不存在导致的错误
                // 给客户端发送文件不存在的消息
                strcpy(buff, "FILE NOT EXIST");
                if(-1 == send(acceptfd, buff, sizeof(buff), 0)){
                    ERR_LOG("send error");
                }
                goto RE_NAME;
            }else{
                ERR_LOG("open error");
            }
        }
        // 文件存在 服务器也要先告诉 客户端
        strcpy(buff, "FILE EXIST");
        if(-1 == send(acceptfd, buff, sizeof(buff), 0)){
            ERR_LOG("send error");
        }

        // TCP粘包问题
        // TCP的底层有一个Nagle算法 作用是将一定短的时间内发往同一个接收端的
        // 多个小的数据包 组装成一个整体 发给对方
        // 但是接收方没法区分数据的边界  就会导致访问有冲突的情况
#if 0
        // 解决方式1：让正文内容和结束标志间隔一定时间发送
        // 循环读取文件内容 发给客户端
        while(0 < (nbytes = read(fd, buff, sizeof(buff)))){
            if(-1 == send(acceptfd, buff, nbytes, 0)){
                ERR_LOG("send error");
            }
        }
        sleep(1);
        // 发送结束标志
        if(-1 == send(acceptfd, "****OVER****", 12, 0)){
            ERR_LOG("send error");
        }
#else
        // 解决方式2：只要保证每次收发数据的数据包一样大 就可以解决粘包问题
        // 只要发数据就应该使用固定大小的数据包(如 文件名 以及文件是否存在的消息 正文  结束标志)
        // 课下自己把前面 文件名 以及文件是否存在的消息 改成使用结构体发送
        // 循环读取文件内容 发给客户端
        msg_t msg;
        while(0 < (nbytes = read(fd, msg.buff, sizeof(msg.buff)))){
            msg.len = nbytes;
            if(-1 == send(acceptfd, &msg, sizeof(msg), 0)){
                ERR_LOG("send error");
            }
        }
        // 发送结束标志  直接发一个len为0的数据包 用来判断即可
        msg.len = 0;
        if(-1 == send(acceptfd, &msg, sizeof(msg), 0)){
            ERR_LOG("send error");
        }
#endif
        close(fd);
        // 关闭套接字
        close(acceptfd);
    }
    // 关闭套接字
    close(sockfd);

    return 0;
}